/** 
 * File: AchievementController.java
 * @author: Adam Phung
 * Date Modified: Nov 20, 2012 
 * Description: Handles displaying earned and unearned achievements for the currently logged in Child
 */

package edu.bu.cs673.superkids123.children.controller;

import java.util.ArrayList;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
import java.util.Set;

import edu.bu.cs673.superkids123.AbstractController;
import edu.bu.cs673.superkids123.AbstractFrame;
import edu.bu.cs673.superkids123.children.DataObjectAccess.StatManager;
import edu.bu.cs673.superkids123.common.DataObjectAccess.ChildManager;
import edu.bu.cs673.superkids123.common.model.Achievement;
import edu.bu.cs673.superkids123.common.model.AchievementRule;
import edu.bu.cs673.superkids123.common.model.AchievementRule.Compare;
import edu.bu.cs673.superkids123.common.model.Child;
import edu.bu.cs673.superkids123.parents.DataObjectAccess.AchievementManager;

// TODO: Auto-generated Javadoc
/**
 * The Class AchievementController.
 */
public class AchievementController extends AbstractController {

	/**
	 * Instantiates a new achievement controller.
	 *
	 * @param mainFrame the main frame
	 */
	public AchievementController(AbstractFrame mainFrame) {
		super(mainFrame);
	}

	/**
	 * Gets the all achievements.
	 *
	 * @return ArrayList of all Achievement
	 */
	public ArrayList<Achievement> getAllAchievements() {
		return AchievementManager.getAchievements();
	}
	
	/**
	 * Gets the all active achievements.
	 * 
	 * @return ArrayList of all active Achievement
	 */
	public ArrayList<Achievement> getAllActiveAchievements() {
		
		ArrayList<Achievement> achievements = new ArrayList<Achievement>();
		
		for (Achievement a : AchievementManager.getAchievements()) {
			if (a.isActive())
				achievements.add(a);
		}
		
		return achievements;
	}
	
	/**
	 * Select achievement.
	 *
	 * @param name the name
	 * @return the achievement
	 */
	public Achievement selectAchievement(String name) {
		return AchievementManager.getAchievementByName(name);
	}
	
	/**
	 * Gets all achievements from currently logged in Child
	 * 
	 * @return list of achievements
	 */
	public List<Achievement> getEarnedAchievements() {
		
		ArrayList<Achievement> achievements = new ArrayList<Achievement>();
		Child child = (Child) authController.getCurrentUser();
		
		for (Achievement a : child.getAchievements()) {
			achievements.add(a);
		}
		
		return achievements;
	}
	
	/**
	 * Returns the total number of achievement points earned by the currently
	 * logged in child
	 * 
	 * @return int Points
	 */
	public int getPoints() {
		Child child = (Child) authController.getCurrentUser();
		return getPoints(child);
	}
	
	/**
	 * Returns the total number of achievement points earned by the passed in
	 * Child.
	 * 
	 * @param child
	 * @return int points
	 */
	public int getPoints(Child child) {
		int points = 0;
		
		Iterator it = child.getAchievements().iterator();
		while (it.hasNext()) {
			Achievement achievement = (Achievement) it.next();
			points += achievement.getPoints();
		}
		
		return points;
	}
	
	/**
	 * Gets the stats of the current logged in child, iterates through them and awards
	 * any achievements not yet rewarded yet to the child. Returns a list of achievements
	 * just earned.
	 * 
	 * @return List of achievements awarded during call
	 */
	public List<Achievement> awardAchievements() {
		
		// Get achievements earned by child
		List<Achievement> earnedAchievements = getEarnedAchievements();
		List<Achievement> awarded = new ArrayList<Achievement>();
		
		for (Achievement a : AchievementManager.getAchievements()) {
			if (!earnedAchievements.contains(a) && rulesValid(a.getRules())) {
				((Child) authController.getCurrentUser()).getAchievements().add(a);
				awarded.add(a);
			}
		}
		
		ChildManager.updateChild((Child) authController.getCurrentUser());
		
		return awarded;
	}
	
	/**
	 * Iterates through each rule and validates it against logged in child's stats.
	 * Returns true if ALL rules have been achieved.  Returns false if ANY rule
	 * has not been achieved.
	 * 
	 * @param rules
	 * @return true if all rules pass else false
	 */
	private boolean rulesValid(Set<AchievementRule> rules) {
		
		Child child = (Child) authController.getCurrentUser();
		Iterator iterator = rules.iterator();
		while(iterator.hasNext()) {
			AchievementRule rule = (AchievementRule) iterator.next();
			
			switch(rule.getMetric()) {
			case Time:
				Date ruleTime = (Date) rule.getValue();
				Date childTime = StatManager.getPlayTime(child, rule.getActivity());
				
				if (!compareValue(ruleTime.getTime(), childTime.getTime(), rule.getComparer())) {
					return false;
				}
				break;
			case Score:
				int ruleScore = (Integer) rule.getValue();
				int childScore[] = StatManager.getPlayScores(child, rule.getActivity());
				
				boolean ruleValid = false;
				for (int i = 0; i < childScore.length; i++) {
					// If any of the scores validates the rule, rule is valid
					if (compareValue(ruleScore, childScore[i], rule.getComparer()))
						ruleValid = true;
				}
				
				if (!ruleValid)
					return false;
				
				break;
			case Count:
				int ruleCount = (Integer) rule.getValue(); 
				int playCount = StatManager.getPlayCount(child, rule.getActivity());
				
				if (!compareValue(ruleCount, playCount, rule.getComparer())) {
					return false;
				}
				break;
			}
		}
		
		return true;
	}
	
	/**
	 * Evaluates a passed value to the rule value by compare type.
	 * 
	 * @param ruleValue
	 * @param childValue
	 * @param compare
	 * @return true if pass, else false
	 */
	private boolean compareValue(long ruleValue, long childValue, Compare compare) {
		
		switch(compare) {
		case LessThan:
			return childValue < ruleValue;
		case LessThanOrEqual:
			return childValue <= ruleValue;
		case Equals:
			return childValue == ruleValue;
		case GreaterThan:
			return childValue > ruleValue;
		case GreaterThanOrEqual:
			return childValue >= ruleValue;
		case NotEquals:
			return childValue != ruleValue;
		}
		
		return false;
	}
}
